package com.eelye.util.winui.window;

import com.eelye.util.winui.manager.ProcessManager;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.ptr.IntByReference;

import java.util.LinkedList;
import java.util.Queue;

import static com.eelye.util.winui.constant.Const.WAIT_INTERVAL;

public class Window {

    /**
     * 所属进程
     */
    protected Process process;

    protected HWND hWnd;

    protected String className;

    public Window(HWND hWnd) {
        this(hWnd, null);
    }

    public Window(HWND hWnd, String className) {
        this.hWnd = hWnd;
        this.className = className;
    }

    public Process getProcess() {
        IntByReference processId = new IntByReference();
        User32.INSTANCE.GetWindowThreadProcessId(hWnd, processId);

        // 接下来，可以尝试获取进程句柄
        HANDLE handle = ProcessManager.getProcessHandle(processId.getValue());
        if (null == handle) {
            return null;
        }

        return new Process(handle);
    }

    public void setFocus() {
        User32.INSTANCE.SetForegroundWindow(this.hWnd);
    }


    public Window findChild(int id, String className, boolean isEqual, int timeout) {
        int elapsed = timeout;

        while (elapsed > 0) {
            elapsed -= 5;

            Window window = findChild(id, className, isEqual);
            if (null != window) {
                return window;
            }

            try {
                Thread.sleep(WAIT_INTERVAL);
            } catch (InterruptedException e) {
                break;
            }
        }

        return null;
    }
    public Window findChild(int id, String className, boolean isEqual) {
        User32 user32 = User32.INSTANCE;
        Queue<HWND> queue = new LinkedList<>();
        HWND[] foundWnd = new HWND[1];

        // 将初始窗口加入队列
        queue.offer(hWnd);

        while (!queue.isEmpty()) {
            HWND currentHWnd = queue.poll();

            user32.EnumChildWindows(currentHWnd, (subWnd, arg) -> {
                String currentClsName = Window.getClassName(subWnd);
                boolean clsFound = isEqual ? currentClsName.equals(className) : currentClsName.contains(className);
                if (clsFound) {
                    int foundId = Window.getId(subWnd);
                    if (foundId == id) {
                        foundWnd[0] = subWnd;

                        return false;
                    }
                }

                // 没有找到，放入队列继续找其孙窗口
                queue.offer(subWnd);

                return true; // 继续枚举
            }, null);

            if (null != foundWnd[0]) {
                break;
            }
        }

        if (null == foundWnd[0]) {
            return null;
        }

        return WindowFactory.buildWindow(foundWnd[0], className);
    }

    public String getText() {
        return getText(this.hWnd);
    }

    public static String getText(HWND hWnd) {
        int size = User32.INSTANCE.GetWindowTextLength(hWnd);
        char[] windowText = new char[size + 1]; // 假设窗口标题不超过512个字符
        User32.INSTANCE.GetWindowText(hWnd, windowText, size + 1);

        return Native.toString(windowText).trim();
    }

    public static int getId(HWND hWnd) {
        return User32.INSTANCE.GetWindowLong(hWnd, User32.GWL_ID);
    }

    public String getClassName() {
        return getClassName(hWnd);
    }

    public static String getClassName(HWND hWnd) {
        int bufferSize = 64; // 初始化一个较小的缓冲区大小
        char[] windowClassBuffer;

        while (true) {
            windowClassBuffer = new char[bufferSize];

            int result = User32.INSTANCE.GetClassName(hWnd, windowClassBuffer, bufferSize);
            if (result != 0) { // 成功获取类名
                // 如果实际写入的字符数小于缓冲区大小，则result就是类名长度
                if (result < bufferSize) {
                    break;
                } else { // 缓冲区太小，需要增大
                    bufferSize *= 2; // 翻倍缓冲区大小以尝试再次获取
                }
            } else { // 获取失败，可能窗口不存在或权限问题
                throw new RuntimeException("Failed to get window class name, error code: " + Native.getLastError());
            }
        }

        return Native.toString(windowClassBuffer).trim();
    }

    public HWND getHWnd() {
        return hWnd;
    }
}
